package com.aizuda.easyManagerTool.service.monitor.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.aizuda.easy.security.domain.Rep;
import com.aizuda.easy.security.domain.Req;
import com.aizuda.easyManagerTool.config.CiphertextTypeHandler;
import com.aizuda.easyManagerTool.domian.dto.PageDTO;
import com.aizuda.easyManagerTool.domian.entity.monitor.MonitorDataEntity;
import com.aizuda.easyManagerTool.domian.vo.PageVO;
import com.aizuda.easyManagerTool.domian.vo.setting.SettingUserVO;
import com.aizuda.easyManagerTool.domian.vo.socket.SocketMessageVO;
import com.aizuda.easyManagerTool.mapper.monitor.MonitorDataIndexItemMapper;
import com.aizuda.easyManagerTool.mapper.monitor.MonitorDataIndexMapper;
import com.aizuda.easyManagerTool.mapper.monitor.MonitorDataMapper;
import com.aizuda.easyManagerTool.service.monitor.MonitorAlarmService;
import com.aizuda.easyManagerTool.service.monitor.MonitorDataService;
import com.aizuda.easyManagerTool.service.socket.impl.SocketSessionManager;
import com.aizuda.easyManagerTool.util.AssertUtil;
import com.aizuda.easyManagerTool.util.ThreadPoolUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
@Slf4j
public class MonitorDataServiceImpl extends ServiceImpl<MonitorDataMapper, MonitorDataEntity> implements MonitorDataService {

    @Resource
    MonitorDataMapper monitorDataMapper;
    @Resource
    MonitorDataIndexMapper monitorDataIndexMapper;
    @Resource
    MonitorDataIndexItemMapper monitorDataIndexItemMapper;
    SocketMessageVO<String> socketMessageVO = new SocketMessageVO<String>();
    @Resource
    MonitorListManagerService monitorListManagerService;
    private ThreadPoolUtil executorService = new ThreadPoolUtil("监控拉取数据-");
    @Resource
    MonitorAlarmService monitorAlarmService;
    @org.springframework.beans.factory.annotation.Value("${config.data-pull-time}")
    Integer pullTime;

    @Override
    public Rep<PageVO<MonitorDataEntity>> find(Req<PageDTO<MonitorDataEntity>, SettingUserVO> req) {
        PageDTO<MonitorDataEntity> pageDTO = req.getData();
        Page<MonitorDataEntity> page = new Page<MonitorDataEntity>(pageDTO.getCurrent(), pageDTO.getSize());
        // 查询 唯一条件是要根据 权限查询
        MonitorDataEntity monitorDataEntity = pageDTO.getData() ;
        IPage<MonitorDataEntity> serverListEntrtyIPage = monitorDataMapper.find(page, ObjectUtil.isEmpty(monitorDataEntity)? new MonitorDataEntity():monitorDataEntity);
        PageVO<MonitorDataEntity> pageVO = new PageVO<MonitorDataEntity>(pageDTO)
                .setTotal(serverListEntrtyIPage.getTotal())
                .setRecords(serverListEntrtyIPage.getRecords());
        return Rep.ok(pageVO);
    }

    @Override
    public Rep<MonitorDataEntity> edit(Req<MonitorDataEntity, SettingUserVO> req) {
        MonitorDataEntity data = req.getData();
        editPrivate(data);
        return Rep.ok();
    }

    @Override
    public void edit(MonitorDataEntity data){
        // 查询
        data.setMdType("json");
        data.setMdStart(false);
        data.setMdSystem(true);
        data.setMdPullTime(5);
        MonitorDataEntity old = monitorDataMapper.findByUrl(CiphertextTypeHandler.encrypt(data.getMdUrl()),data.getTenantId());
        if(ObjectUtil.isNotEmpty(old)){
            data.setId(old.getId());
            data.setCreateTime(old.getCreateTime());
            data.setUpdateTime(old.getUpdateTime());
        }
        editPrivate(data);
    }

    private void editPrivate(MonitorDataEntity data){
        AssertUtil.objIsNull(data.getMdUrl(),"URL不能为空");
        AssertUtil.objIsNull(data.getMdPullTime(),"拉取间隔不能为空");
        AssertUtil.objIsNull(data.getMdTitle(),"标题不能为空");
        AssertUtil.objIsNull(data.getMdType(),"数据类型不能为空");
        saveOrUpdate(data);
        // 判断是否启动，启动需要吧所有的index加入缓存
        if(data.getMdStart()){
            monitorListManagerService.updateMonitorData(data.getId());
        }else{
            MonitorListManager.remove(data.getId());
        }
    }

    @Override
    public Rep<MonitorDataEntity> del(Req<MonitorDataEntity, SettingUserVO> req) {
        MonitorDataEntity data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        MonitorDataEntity monitorDataEntity = monitorDataMapper.selectById(data.getId());
        delPrivate(monitorDataEntity);
        return Rep.ok();
    }

    @Override
    public void del(String url,Integer tenantId) {
        MonitorDataEntity old = monitorDataMapper.findByUrl(CiphertextTypeHandler.encrypt(url),tenantId);
        if(ObjectUtil.isEmpty(old)){
            return;
        }
        delPrivate(old);
    }

    private void delPrivate(MonitorDataEntity monitorDataEntity){
        MonitorListManager.remove(monitorDataEntity.getId());
        monitorDataMapper.deleteById(monitorDataEntity.getId());
        monitorDataIndexMapper.deleteByDataId(monitorDataEntity.getId());
        monitorDataIndexItemMapper.deleteByDataId(monitorDataEntity.getId());
    }

    @Override
    public Rep<Map<String,Object>> getUrl(Req<MonitorDataEntity, SettingUserVO> req) {
        MonitorDataEntity data = req.getData();
        AssertUtil.objIsNull(data.getMdUrl(),"url地址不能为空");
        AssertUtil.objIsNull(data.getMdType(),"请选择数据类型");
        Map<String, Object> map = new HashMap<>();
        try {
            map = urlToMap(data.getMdUrl(), data.getMdType());
        }catch(Exception e){
            return Rep.error(500,"无法连接，请查看服务是否启动或配置是否正确");
        }
        return Rep.ok(map);
    }

    @Override
    public void exec() {
        String time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
        MonitorListManager.getValues().forEach(item -> {
            // 得到json
            executorService.execute(() -> {
                // 数据转换
                MonitorListManager.MonitorData indexItem = MonitorListManager.getIndexItem(item.getId());
                Map<String, Object> map = null;
                try {
                    map = urlToMap(item.getUrl(), indexItem.getType());
                }catch (Exception e){
                    log.error("服务无法连接, {}",item);
                    return;
                }
                List<MonitorListManager.MonitorDataIndex> monitorDataIndices = dataConversion(map,indexItem,time);
                // socket 推送
                try {
                    SocketMessageVO<Object> messageVO = BeanUtil.copyProperties(socketMessageVO, SocketMessageVO.class );
                    messageVO.setType("monitor:look");
                    messageVO.setObj(monitorDataIndices);
                    SocketSessionManager.sendToTenant(indexItem.getTenantId().toString()+":monitor", JSONUtil.toJsonStr(messageVO));
                }catch (Exception e){
                    e.printStackTrace();
                    log.error("5s 输出发送失败 {}",monitorDataIndices);
                    return;
                }
                // 告警保存
                monitorAlarmService.save(indexItem);
            });
        });
    }

    private List<MonitorListManager.MonitorDataIndex> dataConversion(Map<String, Object> map,MonitorListManager.MonitorData indexItem,String time){
        String jsonStr = JSONUtil.toJsonStr(map);
        // 数据转换
        return indexItem.getDataIndex().parallelStream().filter(i -> ObjectUtil.isNotEmpty(i.getId())).map(i -> {
            String value = "0.00";
            i.setTime(time);
            if(StrUtil.isNotEmpty(i.getExp()) && StrUtil.isNotEmpty(jsonStr)){
                Context context = Context.create();
                Value result = context.eval("js", "let json = " + jsonStr + ";" + i.getExp());
                if (!result.isNull()) {
                    value = result.toString();
                }
            }
            i.setValue(Double.parseDouble(value));
            if(ObjectUtil.isEmpty(i.getThreshold())){
                return i;
            }
            if(!i.getMdiAlarm()){
                return i;
            }
            if(i.getThreshold().compareTo(i.getValue()) <= 0){
                i.setCurrentFrequency(i.getCurrentFrequency()+1);
            }else{
                i.setCurrentFrequency(0);
            }
            return i;
        }).collect(Collectors.toList());
    }

    private Map<String,Object> urlToMap(String url,String type){
        Map<String,Object> map = new ConcurrentHashMap<>();
        String s = HttpUtil.get(url, pullTime);
        switch (type){
            case "metrics": map = convertToJSON(s); break;
            case "json": map = JSONUtil.toBean(s,Map.class);
            default: break;
        }
        return map;
    }

    public static Map<String,Object> convertToJSON(String prometheusMetrics) {
        String[] lines = prometheusMetrics.trim().split("\n");
        Map<String, Object> metric = new HashMap<>();
        Arrays.stream(lines).filter(item -> !item.contains("#"))
                .forEach(item -> {
                    String[] s = item.split(" ");
                    for(int i=1;i< s.length - 1;i++){
                        s[0] = s[0]+ s[i];
                    }
                    String key = s[0].substring(0, s[0].indexOf("{") != -1? s[0].indexOf("{") : s[0].length()).trim();
                    String value = s[s.length - 1].trim();
                    Map<String, Object> kv = new HashMap<>();
                    kv.put("value",Double.parseDouble(value));
                    if(s[0].indexOf("{") != -1) {
                        String[] split = s[0].replace(key, "")
                                .replace("{", "")
                                .replace("}", "").split(",");
                        Arrays.stream(split).forEach(ik -> {
                            try {
                                String[] ikSplit = ik.split("=");
                                kv.put(ikSplit[0], ikSplit[1].replace("\"",""));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        });
                    }
                    Object keyObj = metric.get(key);
                    if(ObjectUtil.isEmpty(keyObj)){
                        metric.put(key,kv);
                    }else{
                        Object o = metric.get(key);
                        if(o instanceof List){
                            ((List) o).add(kv);
                        }else{
                            ArrayList<Object> objects = new ArrayList<>();
                            objects.add(kv);
                            objects.add(o);
                            metric.put(key,objects);
                        }
                    }
                });
        return metric;
    }

}
